#include "cgi_param.h"

#include <cstdlib>
using namespace std;

/**
 * Look-up table for hex digits.
 */
static const int HEX_TABLE[256] =
  {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, 0, 1,
   2, 3, 4, 5, 6, 7, 8, 9, -1, -1,
   -1, -1, -1, -1, -1, 10, 11, 12, 13, 14,
   15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, 10, 11, 12,
   13, 14, 15, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1};

/**
 * Unescape URL-escapes and plus signs.
 */
static string unescape(const string& escaped) {
  string result;
  const size_t n = escaped.size();
  const size_t lastEscapeIndex = (n > 2) ? n - 3 : 0;
  result.reserve(n);
  for (size_t i = 0; i < n; ++i) {
    char c = escaped[i];
    if ('+' == c) {
      result += ' ';
    } else if ('%' == c && i < lastEscapeIndex) {
      // Don't handle non-hex chars here to avoid
      // special-casing '+' again here.
      const int h1 = HEX_TABLE[escaped[i + 1]];
      const int h2 = HEX_TABLE[escaped[i + 2]];
      if (h1 >= 0 && h2 >= 0) {
        result += static_cast<char>(h1 << 4 | h2);
        i += 2;
      } else {
        result += c;
      }
    } else {
      result += c;
    }
  }
  result.resize(result.size());
  return result;
}

/**
 * Parse the given query string, allowing multiple values
 * per name.
 */
static
tr1::unordered_multimap<string, string>
parseQueryParameters(const string& queryString) {
  typedef tr1::unordered_multimap<string, string>::value_type value_type;
  tr1::unordered_multimap<string, string> params;
  const size_t end = queryString.size();
  size_t start = 0;
  while (start < end) {
    size_t next = queryString.find('&', start);
    if (string::npos == next) {
      next = end;
    }
    const string param(queryString.substr(start, next - start));
    const size_t equalsSign = param.find('=');
    if (string::npos != equalsSign) {
      params.insert(value_type(param.substr(0, equalsSign),
                               param.substr(equalsSign + 1)));
    } else {
      params.insert(value_type(param, ""));
    }
    start = next + 1;  // Danger of overflow here?
  }
  return params;
}

CgiParam::CgiParam(const string& queryString)
  : params(parseQueryParameters(queryString)) {}

CgiParam CgiParam::fromEnv() {
  const char* env = getenv("QUERY_STRING");
  if (!env) {
    env = "";
  }
  return CgiParam(string(env));
}

bool CgiParam::has(const string& name) {
  return static_cast<bool>(params.count(name));
} 
